/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simcreature;

import javax.swing.ImageIcon;
import javax.swing.JLabel; //imports JLabel library
import java.util.Iterator;
import java.lang.Math;
import java.lang.Integer;
import java.util.Random;

/**
 *
 * @author Fari1Mat
 */
public class Creature extends Lifeform {
    public static final int CARNIVORE = 0;
    public static final int OMNIVORE = 1;
    public static final int HERBIVORE = 2;


    public static final int FIGHT = 0;
    public static final int FLIGHT = 1;
    public static final int RANDOM = 2;

    //Creature statistics. Once we implement evolution these won't be "final"
    final int type;
    final int behavior;
    int health;
    final int maxHealth;
    final int strength;
    final int speed;
    final int range;
    final boolean canGroup;
    final boolean canCannibalize;
    int targetIndex;
    Random rand = new Random();

    ImageIcon dead = new ImageIcon(Board.class.getResource("resources/dead.gif"));
    ImageIcon empty = new ImageIcon(Board.class.getResource("resources/grass.gif"));

    Creature(){
        super(0,0,0,"");
        this.type = 0;
        this.behavior = 0;
        this.health = 0;
        this.maxHealth = 0;
        this.strength = 0;
        this.speed = 0;
        this.range = 0;
        this.canGroup = false;
        this.canCannibalize = false;
        Board.creatureList.add(this);

    }

    Creature(int x, int y, int health, int type, int behavior, int size, int strength, int speed, int range, boolean canGroup, boolean canCannibalize, String name) {
        super(x, y, size, name);
        // The food preference of the creature
        this.type = type;
        // The aggressiveness of the creature
        this.behavior = behavior;
        // The health of the creature
        this.health = health;
        // The maximum health of the creature
        this.maxHealth = health;
        // The maximum damage a creature can do
        this.strength = strength;
        // How fast a creature moves
        this.speed = speed;
        // How far the creature can see
        this.range = range;
        // Whether the creature can group
        this.canGroup = canGroup;
        // Whether the creature can cannibalize
        this.canCannibalize = canCannibalize;

        Board.creatureList.add(this);
    }


 /*
  * The creature thread. I decided to use threads for creatures since I wanted
  * every creature to act on it's own without following a set schedule of creatures,
  * allowing a more random/distinct creature movement and attack speed.
  *
  * This, ultimately, caused some issues with health and coordination between the
  * various creatures but I believe this could easily be fixed with more time/education
  * on streams and thread safety.
 */
    @Override
    public void run() {
        while (Board.creatureList.get(Board.creatureList.indexOf(this)).health > 0) {
            try {
                chooseMove();
                sleep(10000/this.speed); // do nothing for 1 second
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        Board.creatureList.remove(this);
        //Board.update(this.x, this.y, dead);
        this.stop();
    }

 /*
  * Moves to the designated position.
 */
    void move(int x, int y) {
/*        Creature targetC = null;
        Vegetation targetV = null;
        int distanceC = 0;
        int distanceV = 0;

        if(checkCreatureRange())
            targetC = Board.creatureList.get(this.targetIndex);
        if(checkVegetationRange())
            targetV = Board.vegetationList.get(this.targetIndex);

        if(targetC != null)
            distanceC = Math.abs(targetC.x - this.x) + Math.abs(targetC.y - this.y);
        if(targetV != null)
            distanceV = Math.abs(targetV.x - this.x) + Math.abs(targetV.y - this.y);

        if(distanceC == 1 || distanceV == 1 || x < 0 || y < 0 || x >= Board.board.length || y >= Board.board.length) {
            moveRandomly();
        }
        else */
        if(x < 0 || y < 0 || x >= Board.board.length || y >= Board.board.length) {
            moveRandomly();
        }
        else {
            Board.update(this.x,this.y,empty);
            Board.update(x,y,this.icon);
            //Board.board[this.x][this.y].hasCreature(false);
            //Board.board[x][y].hasCreature(true);
            this.x = x;
            this.y = y;
        }
    }

 /*
  * Essentially the brains of the ai. Chooses the closest creature as it's target
  * and then responds appropriately based on the creature's attributes. If there is
  * no creature in range, moves around randomly until there is.
 */
    void chooseMove() {
        if (checkCreatureRange()) {
            if (Board.creatureList.get(this.targetIndex).health > 0) {
                switch (this.behavior) {
                    case 0: fight();                     break;
                    case 1: flight();                    break;
                    case 2: if (rand.nextInt(2) == 0)
                                fight();
                            else
                                flight();               break;
                }
            }
            // If a creature target is in range and the creature eats meat and the target is dead and the creature needs health, target it.
            else if((this.type == 0 || this.type == 1) && (Board.creatureList.get(this.targetIndex).health <= 0 && Board.creatureList.get(Board.creatureList.indexOf(this)).health < this.maxHealth)) {
                fight();
            }
            // If a vegetation target is in range and the creature eats vegetation and the creature needs health, target it or eat it.

            else {
                moveRandomly();
            }
        }
        // If a vegetation target is in range and the creature eats vegetation and the creature needs health, target it or eat it.
        else if (checkVegetationRange() && (this.type == 1 || this.type == 2) && Board.creatureList.get(Board.creatureList.indexOf(this)).health < this.maxHealth) {
            // Move towards and eat the vegetation.
            fight();
        }
        else
            moveRandomly();
    }

 /*
  * Finds the closest creature.
 */
    boolean checkCreatureRange() {
        Creature temp;
        Iterator iter = Board.creatureList.iterator();
        int distance;
        this.targetIndex = -1;
        int leastDistance = Board.board.length*2;
        for (;iter.hasNext(); ) {
            temp = (Creature) iter.next();
            distance = Math.abs(temp.x - this.x) + Math.abs(temp.y - this.y);
            if (distance < leastDistance && distance > 0)
            {
                leastDistance = distance;
                this.targetIndex = Board.creatureList.indexOf(temp);
            }
        }
        if (leastDistance < this.range)
            return true;
        else
            return false;
    }

    boolean checkVegetationRange() {
        Vegetation temp;
        Iterator iter = Board.vegetationList.iterator();
        int distance;
        this.targetIndex = -1;
        int leastDistance = Board.board.length*2;
        for (;iter.hasNext(); ) {
            temp = (Vegetation) iter.next();
            distance = Math.abs(temp.x - this.x) + Math.abs(temp.y - this.y);
            if (distance < leastDistance && distance > 0)
            {
                leastDistance = distance;
                this.targetIndex = Board.vegetationList.indexOf(temp);
            }
        }
        if (leastDistance < this.range)
            return true;
        else
            return false;
    }

 /*
  * Creatures with the fight attribute set move towards other creatures.
  * If a creature that is normally flight sees a dead creature, is a carnivore
  * or an omnivore and needs health, it will move towards the corpse.
  * Also, an herbivore or omnivore creature uses this method to move towards food.
 */
    void fight() {
        Creature target = Board.creatureList.get(this.targetIndex);
        if ((Math.abs(target.x - this.x) == 1 && Math.abs(target.y - this.y) == 0) ||
            (Math.abs(target.x - this.x) == 0 && Math.abs(target.y - this.y) == 1)) {
            if(target.health > 0)
                attack(target);
            else if(this.health < this.maxHealth)
                eat(target);
            else
                chooseMove();
        }
        else {
            if (Math.abs(target.x - this.x) > Math.abs(target.y - this.y)) {
                if (target.x > this.x)
                    this.move(this.x+1,this.y);
                else
                    this.move(this.x-1,this.y);
            }
            else {
                if (target.y > this.y)
                    this.move(this.x,this.y+1);
                else
                    this.move(this.x,this.y-1);
            }
        }
    }

 /*
  * Creatures with the the flight attribute set run away from other creatures.
 */
    void flight() {
        Creature target = Board.creatureList.get(this.targetIndex);
        if (Math.abs(target.x - x) > Math.abs(target.x - this.y)) {
            if (target.x > this.x)
                this.move(this.x-1,this.y);
            else
                this.move(this.x+1,this.y);
        }
        else {
            if (target.y > this.y)
                this.move(this.x,this.y-1);
            else
                this.move(this.x,this.y+1);
        }
    }

 /*
  * The creature attacks the creature with damage calculated
  * exponentially in regards to size and strength.
 */
    void attack(Creature target) {

        
        if ((target.health - this.strength*this.size) <= 0)
            target.health = 0;
        else
            target.health = target.health - this.strength*this.size;
        Board.creatureList.set(Board.creatureList.indexOf(target),target);
        if (target.health == 0) {
            Board.update(target.x, target.y, dead);
            //target.health = -1;
        }
    }

 /**
  * For each point of size that the creature eats, five points of health are gained.
  * The dead creature is removed from the board once fully eaten.
 */
    void eat(Creature target) {
        this.health = this.health + 5;
        target.size = target.size - 1;

        if(target.size <= 0) {
            // target.stop();
            Board.update(target.x, target.y, empty);
        }
    }

    void moveRandomly() {
        switch (rand.nextInt(4)) {
            case 0: move(this.x+1,this.y); break;
            case 1: move(this.x-1,this.y); break;
            case 2: move(this.x,this.y+1); break;
            case 3: move(this.x,this.y-1); break;
        }
    }

    public int getType(){
		return this.type;
	}
    public int getBehavior(){
		return this.behavior;
	}
    public int getHealth(){
		return this.health;
	}
    public int getMaxHealth(){
		return this.maxHealth;
	}
    public int getStrength(){
		return this.strength;
	}
    public int getSpeed(){
		return this.speed;
	}
    public int getRange(){
		return this.range;
	}
    public boolean getCanGroup(){
		return this.canGroup;
	}
    public boolean getCanCannibalize(){
		return this.canCannibalize;
	}
	public int getSize(){
		return this.size;
	}

}